home *** CD-ROM | disk | FTP | other *** search
- /*
- * map.c
- *
- * This program draws three types of map projections - Perspective,
- * Modified Perspective, and Azimuthal Equidistant on IBM PC's.
- *
- * Version 2.7, 7 April 1991
- * by Steve R. Sampson, N5OWK
- *
- * Based on a program and article by William D. Johnston
- * Copyright (c) May-June 1979 BYTE, All Rights Reserved
- *
- * Compiled using Borland C++
- */
-
- #include <stdio.h>
- #include <conio.h>
- #include <fcntl.h>
- #include <dos.h>
- #include <math.h>
- #include <string.h>
- #include <stdlib.h>
- #include <graphics.h>
-
- typedef char bool;
-
- /* Program Constants */
-
- #define FALSE (bool) 0
- #define TRUE (bool) (!FALSE)
-
- #define PI (3.141593F) /* That good old constant */
- #define HALFPI (1.570796F) /* How about 90 Degrees... */
- #define TWOPI (2.0F * PI) /* Two Pi - alias 360 Degrees */
- #define RADIAN (180.0F / PI) /* One radian */
- #define TWO (2.0F / RADIAN) /* 2 Degrees in radians */
- #define TEN (10.0F / RADIAN) /* 10 degrees in radians */
- #define EIGHTY (80.0F / RADIAN) /* 80 degrees in radians */
- #define EARTH (6378.0F) /* Mean radius of earth (Kilometers) */
-
- /* Program Globals */
-
- FILE *fp;
-
- struct { /* Binary Database Format */
- int code, lat, lon;
- } coord;
-
- float Angle, maxplot, Center_lat, Center_lon, lat, lon, distance,
- sin_of_distance, cos_of_distance, sin_of_center_lat, cos_of_center_lat,
- scale, g, h2, facing_azimuth, aspect;
-
- int Option, Center_x, Center_y, Grid_color, Level = 5;
- int GraphDriver = DETECT, GraphMode;
-
- char EscapeTxt[] = "Press ESC to exit, any key to continue";
-
- char optstring[] = "bcd:gilm:rsxy?";
- char database[128] = "mwdbii"; /* default name 'MWDBII' */
- /* leave room for pathname */
- bool boundaries = TRUE, /* defaults to Boundaries, Islands */
- countries = FALSE,
- grids = FALSE,
- islands = TRUE,
- lakes = FALSE,
- rivers = FALSE,
- states = FALSE,
- colors = FALSE;
-
- /* Forward Declarations, Prototypes */
-
- extern int directvideo;
-
- extern int getopt(int, char **, char *);
- extern int optind, opterr;
- extern char *optarg;
-
- static double dm2dec(double);
- static void grid(void), plotmap(void), prompts(void), query(void);
- static bool compute(float *, float *, int *, int *), move(int);
- static int quit(void);
-
- void main(argc, argv)
- int argc;
- char *argv[];
- {
- int i, err, xasp, yasp;
-
- registerbgidriver(EGAVGA_driver); /* IBM Drivers */
- registerbgidriver(Herc_driver);
- registerbgidriver(CGA_driver);
- registerbgifont(small_font);
-
- setcbrk(TRUE); /* Allow Control-C checking */
- ctrlbrk(quit); /* Execute quit() if Control-C detected */
-
- while ((i = getopt(argc, argv, optstring)) != -1) {
- switch (i) {
- case 'b':
- boundaries = FALSE;
- break;
- case 'c':
- countries = TRUE;
- break;
- case 'd':
- strcpy(database, optarg);
- break;
- case 'g':
- grids = TRUE;
- break;
- case 'i':
- islands = FALSE;
- break;
- case 'l':
- lakes = TRUE;
- break;
- case 'm':
- Level = atoi(optarg);
- break;
- case 'r':
- rivers = TRUE;
- break;
- case 's':
- states = TRUE;
- break;
- case 'x':
- colors = TRUE;
- break;
- case 'y':
- directvideo = 0;
- break;
- case '?':
- default:
- printf("Usage: map [/bcdgilmrsxy]\n\n");
- printf(" /b Boundaries Off\n");
- printf(" /c Countries On\n");
- printf(" /dn Database ('MWDBII' Default)\n");
- printf(" /g Grid lines On\n");
- printf(" /i Islands Off\n");
- printf(" /l Lakes On\n");
- printf(" /mn Map Resolution (5 Default)\n");
- printf(" /r Rivers On\n");
- printf(" /s States On\n");
- printf(" /x Colors On\n");
- printf(" /y BIOS Video Mode On\n\n");
- printf("Defaults to Boundaries and Islands On\n");
-
- exit(0);
- }
- }
-
- if ((fp = fopen(database, "rb")) == (FILE *)NULL) {
- printf("\007Error: Can't locate Database '%s'\n", database);
- exit(1);
- }
-
- setvbuf(fp, NULL, _IOFBF, 32767);
-
- initgraph(&GraphDriver, &GraphMode, "");/* initialize graphics */
- err = graphresult();
-
- if (err < 0) {
- printf("Graphics Error - %s\n", grapherrormsg(err));
- exit(1);
- }
-
- if (GraphMode == VGAHI) { /* Use medium resolution */
- GraphMode = VGAMED; /* for 2 graphic pages */
- setgraphmode(GraphMode); /* and then enable it */
- }
-
- Center_x = getmaxx() / 2; /* get screen size for x, y */
- Center_y = getmaxy() / 2;
- getaspectratio(&xasp, &yasp); /* squish factor for y axis */
- aspect = (float)xasp / (float)yasp;
-
- restorecrtmode(); /* get back to text mode */
- prompts(); /* get the basic map info */
-
- setgraphmode(GraphMode); /* and go to graphics mode */
-
- if ((GraphMode == CGAHI) || (GraphMode == HERCMONOHI))
- Grid_color = LIGHTGRAY; /* CGA/HERC has two colors */
- else {
- setbkcolor(EGA_BLACK); /* must be EGA or VGA then */
- if (colors)
- Grid_color = EGA_CYAN;
- else
- Grid_color = EGA_LIGHTGRAY;
- }
-
- if (grids)
- grid(); /* draw lat & long ref lines */
-
- setcolor(LIGHTGRAY);
-
- /*
- * See if data plotting is even needed
- */
-
- if (boundaries || islands || countries || lakes || rivers || states)
- plotmap(); /* display map on screen */
-
- setcolor(LIGHTGRAY);
- outtextxy(0, getmaxy() - 10, "Done");
- getch(); /* wait for end of viewing */
-
- quit();
- }
-
- /*
- * Return to operator
- */
-
- #pragma warn -rvl
- static int quit()
- {
- closegraph(); /* graphics off */
- fclose(fp); /* close database file */
-
- exit(0);
- }
- #pragma warn +rvl
-
- /*
- * Operator prompts and input.
- */
-
- static void prompts()
- {
- float ret, altitude;
-
- printf("West Longitudes and South Latitudes are negative\n");
-
- /* input the world Lat & Long that is to be centered on */
- /* then convert the human notation to radians */
-
- do {
- printf("\nLatitude of the map center [+-]dd.mm : ");
- scanf("%f", &ret);
- ret = dm2dec(ret);
- } while (ret > 90.0F || ret < -90.0F);
-
- /* the arcosine function has trouble at 90 degrees */
-
- if (ret == 90.0F)
- Center_lat = 89.9F / RADIAN;
- else if (ret == -90.0F)
- Center_lat = -89.9F / RADIAN;
- else
- Center_lat = ret / RADIAN;
-
- sin_of_center_lat = sin(Center_lat);
- cos_of_center_lat = cos(Center_lat);
-
- do {
- printf("Longitude of the map center [+-]ddd.mm : ");
- scanf("%f", &ret);
- ret = dm2dec(ret);
- } while (ret > 180.0F || ret < -180.0F);
-
- Center_lon = ret / RADIAN;
-
- do {
- printf("\nSelect from the following options:\n\n");
- printf(" 1 - Perspective Projection\n");
- printf(" 2 - Modified Perspective Projection\n");
- printf(" 3 - Azimuthal Equidistant Projection\n\n");
- printf("Choice : ");
- scanf("%d", &Option);
- } while (Option < 1 || Option > 3);
-
- if (Option == 3) {
- /* input the radial distance to map */
-
- do {
- printf("\nArc Distance (1 - 180 degrees) : ");
- scanf("%f", &maxplot);
- } while (maxplot == 0.0F || maxplot > 180.0F);
-
- maxplot /= RADIAN;
- scale = (float) Center_y / maxplot;
- return;
- }
-
- /* input the height above the terrain */
-
- printf("\nObserver altitude (km) : ");
- scanf("%f", &altitude);
-
- if (altitude == 0.0F)
- altitude = 1.0F;
-
- h2 = EARTH + altitude;
- maxplot = acos(EARTH / h2);
-
- /* The operator can orient the world upside down if they want */
-
- do {
- printf("Observer facing azimuth (0 - 359 degrees) : ");
- scanf("%f", &facing_azimuth);
- } while (facing_azimuth < 0.0F || facing_azimuth > 360.0F);
-
- facing_azimuth = -facing_azimuth / RADIAN;
-
- /* Calculate the scale for the polar coordinates */
-
- scale = (float)Center_y / (EARTH * sin(maxplot));
-
- /* For the perspective projection */
-
- g = EARTH * (h2 - EARTH * cos(maxplot));
- }
-
- /*
- * Convert the database to the desired projection by computation.
- *
- * This code is a hand translation from BASIC to C based on Mr. Johnstons
- * code. It is a non-mathematicians idea of what he meant.
- *
- * Return TRUE if offscale else FALSE.
- */
-
- static bool compute(lat, lon, x, y)
- float *lat, *lon;
- int *x, *y;
- {
- float sin_of_lat,
- cos_of_lat,
- abs_delta_lon, /* absolute value */
- delta_lon, /* x distance from center */
- delta_lat, /* y distance from center */
- temp; /* temporary storage */
-
- /* normalize the longitude to +/- PI */
-
- delta_lon = *lon - Center_lon;
-
- if (delta_lon < -PI)
- delta_lon = delta_lon + TWOPI;
-
- if (delta_lon > PI)
- delta_lon = delta_lon - TWOPI;
-
- abs_delta_lon = fabs(delta_lon);
-
- /*
- * If the delta_lon is within .00015 radians of 0 then
- * the difference is considered exactly zero.
- *
- * This also simplifies the great circle bearing calculation.
- */
-
- if (abs_delta_lon <= 0.00015F) {
- delta_lat = fabs(Center_lat - *lat);
-
- if (delta_lat > maxplot)
- return TRUE; /* offscale */
-
- if (*lat < Center_lat)
- Angle = PI;
- else
- Angle = 0.0F;
-
- sin_of_distance = sin(delta_lat);
- cos_of_distance = cos(delta_lat);
- }
-
- /*
- * Check if delta_lon is within .00015 radians of PI.
- */
-
- else if (fabs(PI - abs_delta_lon) <= 0.00015F) {
- delta_lat = PI - Center_lat - *lat;
-
- if (delta_lat > PI) {
- delta_lat = TWOPI - delta_lat;
- Angle = PI;
- } else
- Angle = 0.0F;
-
- if (delta_lat > maxplot)
- return TRUE; /* offscale */
-
- sin_of_distance = sin(delta_lat);
- cos_of_distance = cos(delta_lat);
- }
-
- /*
- * Simple calculations are out, now get cosmic.
- */
-
- else {
- sin_of_lat = sin(*lat);
- cos_of_lat = cos(*lat);
-
- cos_of_distance = sin_of_center_lat * sin_of_lat +
- cos_of_center_lat * cos_of_lat *
- cos(delta_lon);
-
- distance = acos(cos_of_distance);
-
- if (distance > maxplot)
- return TRUE; /* offscale */
-
- sin_of_distance = sin(distance);
-
- temp = (sin_of_lat - sin_of_center_lat * cos_of_distance) /
- (cos_of_center_lat * sin_of_distance);
-
- if (temp < -1.0F || temp > 1.0F)
- return TRUE; /* offscale */
-
- Angle = acos(temp);
-
- if (delta_lon < 0.0F)
- Angle = TWOPI - Angle;
- }
-
- if (facing_azimuth != 0.0F) {
- Angle = Angle - facing_azimuth;
- if (Angle < 0.0F)
- Angle = TWOPI + Angle;
- }
-
- Angle = HALFPI - Angle;
-
- if (Angle < -PI)
- Angle = Angle + TWOPI;
-
- switch (Option) {
- case 1:
- temp = (scale * (g * sin_of_distance)) /
- (h2 - EARTH * cos_of_distance);
- break;
- case 2:
- temp = scale * EARTH * sin_of_distance;
- break;
- case 3:
- temp = scale * distance;
- }
-
- /* convert polar to rectangular, correct for screen aspect */
-
- *x = Center_x + (int)(temp * cos(Angle));
- *y = Center_y - (int)(temp * sin(Angle) * aspect);
-
- return FALSE;
- }
-
- /*
- * Read the database and plot points or lines.
- */
-
- static void plotmap()
- {
- float lat, lon;
- int x, y;
- bool point;
-
- point = TRUE;
- while (fread(&coord, sizeof coord, 1, fp) > 0) {
- if (kbhit()) {
- getch();
- query();
- }
-
- /*
- * Skip data that has been optioned out.
- */
-
- if (coord.code < Level)
- continue;
-
- if (coord.code > 5) { /* must be a header */
- point = TRUE;
-
- switch (coord.code / 1000) {
- case 1:
- if (boundaries) {
- if (colors)
- setcolor(EGA_LIGHTGRAY);
- break;
- } else {
- if (move(1))
- return;
- continue;
- }
- case 2:
- if (countries) {
- if (colors)
- setcolor(EGA_BROWN);
- break;
- } else {
- if (move(2))
- return;
- continue;
- }
- case 4:
- if (states) {
- if (colors)
- setcolor(EGA_BROWN);
- break;
- } else {
- if (move(4))
- return;
- continue;
- }
- case 5:
- if (islands) {
- if (colors)
- setcolor(EGA_LIGHTGRAY);
- break;
- } else {
- if (move(5))
- return;
- continue;
- }
- case 6:
- if (lakes) {
- if (colors)
- setcolor(EGA_BLUE);
- break;
- } else {
- if (move(6))
- return;
- continue;
- }
- case 7:
- if (rivers) {
- if (colors)
- setcolor(EGA_GREEN);
- break;
- } else {
- if (move(7))
- return;
- continue;
- }
- }
- }
-
- /* Convert database minutes of a degree to radians */
-
- lat = (float) coord.lat / 60.0F / RADIAN;
- lon = (float) coord.lon / 60.0F / RADIAN;
-
- if (compute(&lat, &lon, &x, &y)) {
- point = TRUE; /* offscale */
- continue;
- }
-
- if (point) {
- putpixel(x, y, getcolor());/* put down a dot */
- moveto(x, y);
- point = FALSE;
- }
- else
- lineto(x, y); /* connect the dots */
- }
- }
-
- /*
- * Move to next database characteristic
- *
- * Returns TRUE for end of file, else FALSE
- */
-
- static bool move(n)
- int n;
- {
- while (fread(&coord, sizeof coord, 1, fp) > 0) {
- if ((coord.code > 5) && ((coord.code/1000) != n))
- return FALSE;
- }
-
- return TRUE;
- }
-
- /*
- * Convert +-ddd.mm
- *
- * Change degrees and minutes to decimal
- */
-
- static double dm2dec(degmin)
- double degmin;
- {
- float t;
-
- t = (int)degmin; /* get the integer part */
- degmin -= t; /* now the fractional part */
- degmin /= .60; /* convert minutes to decimal */
-
- return (degmin + t); /* add the two */
- }
-
- /*
- * Draw grid lines
- *
- * Azimuthal Equidistant - Draw a radar scope presentation
- * on EGA/VGA, regular grid on CGA and HERC - From -180 to
- * +180 Degrees (Longitude Lines), and +80 to -80 Degrees
- * (Latitude Lines).
- */
-
- static void grid()
- {
- int x, y, pass1;
- float lat, lon, bearing, t;
-
- setcolor(Grid_color);
-
- if (Option == 3 && GraphMode != CGAHI && GraphMode != HERCMONOHI) {
- setvisualpage(1);
- setactivepage(0);
-
- circle(Center_x, Center_y, Center_y); /* inner ring */
- circle(Center_x, Center_y, Center_y+10); /* outer ring */
-
- /*
- * Ten Degree Full size ticks
- */
-
- t = (float)(Center_y+10);
- for (bearing = 0.0F; bearing < TWOPI; bearing += TEN) {
- moveto(Center_x, Center_y);
- x = Center_x + (int)(t * sin(bearing));
- y = Center_y - (int)(t * cos(bearing) * aspect);
- lineto(x, y);
- }
-
- /*
- * Two Degree Half size ticks
- */
-
- t = (float)(Center_y+5);
- for (bearing = 0.0F; bearing < TWOPI; bearing += TWO) {
- moveto(Center_x, Center_y);
- x = Center_x + (int)(t * sin(bearing));
- y = Center_y - (int)(t * cos(bearing) * aspect);
- lineto(x, y);
- }
-
- /*
- * This routine erases all bearing lines except
- * those between the two circles.
- */
-
- setcolor(EGA_WHITE);
- circle(Center_x, Center_y, Center_y);
-
- settextjustify(CENTER_TEXT, CENTER_TEXT);
- setfillstyle(SOLID_FILL, EGA_BLACK);
-
- floodfill(Center_x, Center_y, EGA_WHITE);
-
- setcolor(Grid_color);
- circle(Center_x, Center_y, Center_y);
-
- /*
- * Mark the center
- */
-
- outtextxy(Center_x, Center_y, "+");
-
- /*
- * Label the Axis
- */
-
- settextstyle(SMALL_FONT, HORIZ_DIR, 4);
-
- outtextxy(Center_x, 20, "360");
- outtextxy(520, Center_y, "090");
- outtextxy(Center_x, 330, "180");
- outtextxy(120, Center_y, "270");
-
- /*
- * Return to defaults
- */
-
- settextstyle(DEFAULT_FONT, HORIZ_DIR, 1);
- settextjustify(LEFT_TEXT, LEFT_TEXT);
-
- setvisualpage(0);
- } else {
- setlinestyle(DOTTED_LINE, NULL, NORM_WIDTH);
-
- for (lon = -PI; lon <= PI; lon += TEN) {
- pass1 = TRUE;
- for (lat = EIGHTY; lat > -EIGHTY; lat -= TEN) {
- if (!compute(&lat, &lon, &x, &y)) {
- if (pass1) {
- putpixel(x, y, Grid_color);
- moveto(x, y);
- pass1 = FALSE;
- } else
- lineto(x, y);
- } else
- pass1 = TRUE;
- }
-
- if (kbhit()) {
- getch();
- query();
- }
- }
-
- for (lat = EIGHTY; lat > -EIGHTY; lat -= TEN) {
- pass1 = TRUE;
- for (lon = -PI; lon <= PI; lon += TEN) {
- if (!compute(&lat, &lon, &x, &y)) {
- if (pass1) {
- putpixel(x, y, Grid_color);
- moveto(x, y);
- pass1 = FALSE;
- } else
- lineto(x, y);
- } else
- pass1 = TRUE;
-
- }
-
- if (kbhit()) {
- getch();
- query();
- }
- }
-
- setlinestyle(SOLID_LINE, NULL, NORM_WIDTH);
- }
- }
-
- static void query()
- {
- int x, y;
-
- sound(1000); /* 1000 Hz for fourth/second */
- delay(250);
- nosound();
-
- x = getx(); /* remember last location */
- y = gety();
- outtextxy(0, getmaxy() - 10, EscapeTxt);
-
- if (getch() == '\033') /* Escape character? */
- quit();
-
- setcolor(BLACK);
- outtextxy(0, getmaxy() - 10, EscapeTxt);
- setcolor(LIGHTGRAY);
- moveto(x, y); /* go back to last location */
- }
-
- /* That is all */